import java.applet.Applet;
import java.awt.Color;
import java.awt.Event;
import java.awt.Font;
import java.awt.Graphics;

public class TravelingSalesmanProblem extends Applet implements Runnable {

	private static final long serialVersionUID = 2652288922506829333L;
	
	private int pocetMiest = 5;
	private int pocetVystupnychNeuronov;
	private Mesto mesta[];
	private VystupnyNeuron vystupneNeurony[];
	private double vahyMedziVystupnymiNeuronmi[][];
	
	private double theta, phi, momentum;
	
	private volatile Thread animator = null;
	private boolean stop = false;
	private int counter;
	
	private void inicializujSiet() {
		theta = 0.5;
		phi = 0.5;
    	momentum = 0.995;

    	pocetMiest = 7;	
		pocetVystupnychNeuronov = pocetMiest * 2;
        
		mesta = new Mesto[pocetMiest];
		for (int i = 0; i < pocetMiest; i++) {
			mesta[i] = new Mesto(Math.random() * 1.00, Math.random() * 1.00);
		}
		
		double alpha = 0.0;      
		vystupneNeurony = new VystupnyNeuron[pocetVystupnychNeuronov];
		
		for (int i = 0; i < pocetVystupnychNeuronov; i++) {
			vystupneNeurony[i] = new VystupnyNeuron(0.5 + 0.5 * Math.cos(alpha), 0.5 + 0.5 * Math.sin(alpha));
			alpha += Math.PI * 2.0 / (double)(pocetVystupnychNeuronov);
		}
      
		vahyMedziVystupnymiNeuronmi = new double[pocetVystupnychNeuronov][pocetVystupnychNeuronov];
      
		prepocitajVahyMedziVystupnymiNeuronmi(theta);        
		
		counter = 0;
	}

	private void prepocitajVahyMedziVystupnymiNeuronmi(double theta) {
		for (int i = 0; i < pocetVystupnychNeuronov; i++) {
			vahyMedziVystupnymiNeuronmi[i][i] = 1.0;
			for (int j = i+1; j < pocetVystupnychNeuronov; j++) {
				vahyMedziVystupnymiNeuronmi[i][j] = Math.exp(-1.0 * (vystupneNeurony[i].vzdialenost(vystupneNeurony[j]) * vystupneNeurony[i].vzdialenost(vystupneNeurony[j])) / (2.0 * theta * theta));
				vahyMedziVystupnymiNeuronmi[j][i] = vahyMedziVystupnymiNeuronmi[i][j];
			}
		}
	}

    public void run() {
    	int indexNahodnehoMesta, indexVystupnehoNeuronu;
    	double xi, yi, minimalnaVzdialenost;
    	int pocet = 0;
    	
    	Thread thread = Thread.currentThread();
        while (!stop && thread == animator) {		//pouzite spravne stopnutie Threadu            
            counter++;
            
            //prezentuj suradnice nahodneho mesta na vstup siete
            indexNahodnehoMesta = (int)(Math.random() * pocetMiest);
            xi = mesta[indexNahodnehoMesta].x + (Math.random() * 0.05) - 0.05 / 2;
            yi = mesta[indexNahodnehoMesta].y + (Math.random() * 0.05) - 0.05 / 2;
            mesta[indexNahodnehoMesta].pocetVyberov++;
            
            //najdi vitaza na vystupnej vrstve - WINNER TAKES ALL
            minimalnaVzdialenost = 100000.0;
            indexVystupnehoNeuronu = -1;
            for (int i = 0; i < pocetVystupnychNeuronov; i++) {
            	double d = (xi - vystupneNeurony[i].wx) * (xi - vystupneNeurony[i].wx) + (yi - vystupneNeurony[i].wy) * (yi - vystupneNeurony[i].wy);
            	if (d < minimalnaVzdialenost) {
            		minimalnaVzdialenost = d;
            		indexVystupnehoNeuronu = i;
            	}
            }                        
            vystupneNeurony[indexVystupnehoNeuronu].update++;
                     
            //adaptacia vah
            for(int i = 0; i < pocetVystupnychNeuronov; i++) {
            	vystupneNeurony[i].wx += (phi * vahyMedziVystupnymiNeuronmi[i][indexVystupnehoNeuronu] * (xi - vystupneNeurony[i].wx));
            	vystupneNeurony[i].wy += (phi * vahyMedziVystupnymiNeuronmi[i][indexVystupnehoNeuronu] * (yi - vystupneNeurony[i].wy));
            }
            
            //zmensi uciace pomery
            phi *= momentum;
            theta *= momentum;
            
            //prepocitaj vahy
            prepocitajVahyMedziVystupnymiNeuronmi(theta);
       		  
            pocet = (pocet++) % 10;
       		  
            if (pocet == 0) {
            	paint(getGraphics());
			
            	try {
            		Thread.sleep(10);
            	} catch (InterruptedException e) {
            		System.err.println("Prerusene uspanie vlakna!!!");
            	};
            }
        }
        animator = null;
    }
    
    public void init() {    	
    	inicializujSiet();	
    }

    private int toXReal(double val) {
    	int w = getSize().width;
    	return (int)(val * ((double) w - 50.0) / 1.00 + 25.0);
    }
    
    private int toYReal(double val) {
    	int h = getSize().height;
    	return (int)(val * ((double) h - 50.0) / 1.00 + 25.0);
    }

    public void paint(Graphics g) {
    	setSize(640, 480);
    	int w = getSize().width;    	
    	int h = getSize().height;
		Font font = new Font("Courier", Font.BOLD, 12);
		g.setFont(font);
		
		//vymaz plochu
        g.setColor(new Color(0x007700));
        g.fillRect(0, 0, w, h);
		
        //nakresli mriezku
        g.setColor(new Color(0xAAAA00));
        for (double i = 0; i < 1.00; i+=(1.00/20.0)) {
        	g.drawLine(toXReal(0.0), toYReal(i), toXReal(1.00), toYReal(i));
        	g.drawLine(toXReal(i), toYReal(0.0), toXReal(i), toYReal(1.00));
        }
        
        //nakresli cestu
        g.setColor(new Color(0xFF0000));
        for (int i = 0; i < pocetVystupnychNeuronov; i++) {
        	g.drawLine(toXReal(vystupneNeurony[i].wx), toYReal(vystupneNeurony[i].wy), toXReal(vystupneNeurony[(i+1) % pocetVystupnychNeuronov].wx), toYReal(vystupneNeurony[(i+1) % pocetVystupnychNeuronov].wy));
        	g.drawString("" + i + "-" + (vystupneNeurony[i].update * 100 / counter) + "%", toXReal(vystupneNeurony[i].wx), toYReal(vystupneNeurony[i].wy));
        }

        g.setColor(new Color(0xFFFFFF));
        
        //vykresli mesta
        for (int i = 0; i < pocetMiest; i++) {
        	g.drawOval(toXReal(mesta[i].x) - 4, toYReal(mesta[i].y) - 4, 8, 8);
        	g.drawString("" + i + "-" + (mesta[i].pocetVyberov * 100 / counter) + "%", toXReal(mesta[i].x), toYReal(mesta[i].y) + 8);
        }
    }

    public void start() { 
        animator = new Thread(this);        
        animator.start();        
    }
    
    public void stop() { 
        animator = null;
    }
    
    public boolean mouseDown(Event e, int x, int y) {
    	if (animator != null) {
    		stop = true;
    	} else { 
    		stop = false; 
    		animator = new Thread(this);
				
    		inicializujSiet();
    		animator.start();
    	} 
    	return true;
    }
}